package com.imooc.controller;

import com.google.gson.Gson;
import com.imooc.api.mq.RabbitMQSMSConfig;
import com.imooc.api.retry.RetryComponent;
import com.imooc.api.task.SMSTask;
import com.imooc.base.BaseInfoProperties;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.Users;
import com.imooc.pojo.bo.RegistLoginBO;
import com.imooc.pojo.mq.SMSContentQO;
import com.imooc.pojo.vo.UsersVO;
import com.imooc.service.UsersService;
import com.imooc.utils.GsonUtils;
import com.imooc.utils.IPUtil;
import com.imooc.utils.JWTUtils;
import com.imooc.utils.SMSUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.UUID;

/**
 * @version 1.0
 * @Author Mr.Yang
 * @Date 2024-08-05 17:05
 * @question: 用户端app登录
 */

@RestController
@RequestMapping("passport")
@Slf4j
public class PassportController extends BaseInfoProperties {

    @Autowired
    private SMSUtils smsUtils;

    @Autowired
    private JWTUtils jwtUtils;

    @Autowired
    private UsersService usersService;

    @Autowired
    private RetryComponent retryComponent;

    @Autowired
    private SMSTask smsTask;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 获取验证码
     * @param mobile
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("getSMSCode")
    public GraceJSONResult getSMSCode(String mobile, HttpServletRequest request) throws Exception {

        if (StringUtils.isBlank(mobile)) {
            return GraceJSONResult.error();
        }

        //获得用户ip
        String userIp = IPUtil.getRequestIp(request);
        log.info("当前ip为：{}", userIp);
        //限制用户只能在60s以内获得一次验证码 【此处就是如果key不存在 就设置 如果存在就不动】 setIfAbsent
        redis.setnx60s(MOBILE_SMSCODE + ":" + userIp, mobile);

        //发送短信 随机6位的验证码 此处写死 123456
        //String code = (int)((Math.random() * 9 + 1) * 100000) + "";
        String code = "123456";
        log.info("验证码为：{}", code);
        //smsUtils.sendSMS(mobile, code);
        /**
         * spring的retry重拾机制
         */
        //retryComponent.sendSmsWithRetry(mobile, code);
        /**
         * 发送短信的异步任务 只不过不是RabbitMQ 而是Spring框架的Async
         * 因为发送短信业务 涉及到了重试机制 不能让其一直在发送短信页面卡着 优化体验
         */
        //smsTask.sendSMSTask(mobile, code);

//        /**
//         * 生产者到交换机的消息确认
//         */
//        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
//
//            /**
//             * 回调函数
//             * @param correlationData rabbitMQ的确认机制--关联确认(Correlated Confirms)在这种模式下,每个发送的消息都会获得一个唯一的序列号
//             * @param ack 交换机是否成功接收到消息 true:成功
//             * @param cause 失败的原因
//             */
//            @Override
//            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
//                log.info("进入 confirm");
//                log.info("correlationData: {}", correlationData.getId());
//
//                if (ack) {
//                    log.info("交换机成功接收到消息！ {}",cause);
//                } else {
//                    log.info("交换机接收消息失败！ {}",cause);
//                }
//
//            }
//        });
//
//        /**
//         * 生产者交换机到队列处的return回调函数
//         * 如果成功发送消息 则该方法不会被触发
//         */
//        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
//
//            /**
//             * return回调函数
//             * @param returnedMessage
//             */
//            @Override
//            public void returnedMessage(ReturnedMessage returnedMessage) {
//                log.info("进入return........");
//                log.info("returnedMessage: {}", returnedMessage.toString());
//            }
//        });


        //使用消息队列异步解耦发送短信
        SMSContentQO contentQO = new SMSContentQO();
        contentQO.setMobile(mobile);
        contentQO.setContent(code);
        rabbitTemplate.convertAndSend(RabbitMQSMSConfig.SMS_EXCHANGE,
                RabbitMQSMSConfig.ROUTING_KEY_SMS_SEND_LOGIN,
                GsonUtils.object2String(contentQO),
                new CorrelationData(UUID.randomUUID().toString()));

        //把验证码存入到redis,用于后续的注册登录进行校验
        //此处还需要在设置一个拦截器 用于去判断手机号60s之内是否接受过验证码了 如果接受过就不允许再往下走了 拦截器放在了api模块
        redis.set(MOBILE_SMSCODE + ":" + mobile, code, 30 * 60);

        return GraceJSONResult.ok();
    }

    /**
     * 用户登录 如果未注册 则先注册
     * @param registLoginBO
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("login")
    public GraceJSONResult login(@Valid @RequestBody RegistLoginBO registLoginBO, HttpServletRequest request) throws Exception {

        String mobile = registLoginBO.getMobile();
        String code = registLoginBO.getSmsCode();

        //从redis中获得验证码进行校验判断是否匹配
        String redisCode = redis.get(MOBILE_SMSCODE + ":" + mobile);
        //验证码为空报错 验证码忽略大小写不一样的话报错equalsIgnoreCase
        if (StringUtils.isBlank(redisCode) || !redisCode.equalsIgnoreCase(code)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.SMS_CODE_ERROR);
        }

        //根据mobile查询数据库,判断用户是否存在
        Users user = usersService.queryMobileIsExist(mobile);
        if (user == null) {
            //如果查询的用户为空,则表示没有注册过,则需要注册信息入库
            user = usersService.createUsers(mobile);
        }

        //保存用户token,分布式会话到redis中
//        String uToken = TOKEN_USER_PREFIX + SYMBOL_DOT + UUID.randomUUID().toString();
//        redis.set(REDIS_USER_TOKEN + ":" + user.getId(), uToken);
        String jwt = jwtUtils.createJWTWithPrefix(new Gson().toJson(user),
//                                                    Long.valueOf(60 * 1000),
                TOKEN_USER_PREFIX);

        //用户登录注册以后,删除redis中的短信验证码
        redis.del(MOBILE_SMSCODE + ":" + mobile);

        //返回用户的信息给前端
        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(user, usersVO);
        usersVO.setUserToken(jwt);

        return GraceJSONResult.ok(usersVO);
    }

    /**
     * 退出
     * @param userId
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("logout")
    public GraceJSONResult logout(@RequestParam String userId, HttpServletRequest request) throws Exception {

        //后端只需要清除用户的token信息即可,前端也需要清除相关的用户信息
//        redis.del(REDIS_USER_TOKEN + ":" + userId);

        return GraceJSONResult.ok();
    }

}
